﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Windows.Forms;

namespace YK.ListItemTransfer
{
    public class ItemTransfer
    {
        #region delegates and events

        delegate void ItemCopiedEventHandler(SPListItem sourceItem, SPListItem destinationItem);
        delegate void ItemNotCopiedEventHandler(SPListItem sourceItem, Exception exception);
        event ItemCopiedEventHandler ItemCopied;
        event ItemNotCopiedEventHandler ItemNotCopied;

        #endregion

        #region constants

        private const string SharePointSeperator = ";#";
        private const string UserParseFormat = "-1;#{0}";
        private const string ListTemplateName = "ItemTransferListTemplateName";
        private const string ListTemplateFileName = "ItemTransferListTemplateName.stp";
        private const string ListTemplateGalleryName = "List Template Gallery";
        private const string CurrentItemTextFormat = "'{0}' named item is copying...";

        #endregion

        #region local variables

        private SPSite sourceSiteCollection;
        private SPWeb sourceSite;
        private SPList sourceList;
        private SPSite destinationSiteCollection;
        private SPWeb destinationSite;
        private SPList destinationList;
        private readonly TransferSetting _TransferSetting;
        private readonly FormMain _MainForm;
        private List<string> ignoreFieldNames = new List<string>() { "Attachments" };
        private List<FieldInfo> assocationFields;
        private Log log;
        private System.Threading.Thread thread;

        #endregion

        #region properties

        public ProgressBar ProgressBar
        {
            get { return _MainForm.progressBar; }
        }

        public string CurrentItem
        {
            set
            {
                if (string.IsNullOrEmpty(value))
                    _MainForm.labelCurrentItem.Text = string.Empty;
                else
                    _MainForm.labelCurrentItem.Text = string.Format(CurrentItemTextFormat, value);
            }
        }

        public bool ButtonStartEnabled
        {
            get
            {
                return _MainForm.buttonStart.Enabled;
            }
            set
            {
                _MainForm.buttonStart.Enabled = value;
            }
        }

        public bool ButtonCancelEnabled
        {
            get
            {
                return _MainForm.buttonCancel.Enabled;
            }
            set
            {
                _MainForm.buttonCancel.Enabled = value;
                _MainForm.stopItemTransferToolStripMenuItem.Enabled = value;
            }
        }

        public bool ButtonPauseEnabled
        {
            get
            {
                return _MainForm.buttonPause.Enabled;
            }
            set
            {
                _MainForm.buttonPause.Enabled = value;
                _MainForm.pauseItemTransferToolStripMenuItem.Enabled = value;
            }
        }

        public bool InputEnabled
        {
            get
            {
                return _MainForm.panelInput.Enabled;
            }
            set
            {
                _MainForm.panelInput.Enabled = value;
            }
        }

        public int TotalCount
        {
            get
            {
                return Convert.ToInt32(_MainForm.labelTotalCount.Text);
            }
            set
            {
                _MainForm.labelTotalCount.Text = value.ToString();
            }
        }

        public int CopiedCount
        {
            get
            {
                return Convert.ToInt32(_MainForm.labelCopiedCount.Text);
            }
            set
            {
                _MainForm.labelCopiedCount.Text = value.ToString();
            }
        }

        public int FailedCount
        {
            get
            {
                return Convert.ToInt32(_MainForm.labelNotCopiedCount.Text);
            }
            set
            {
                _MainForm.labelNotCopiedCount.Text = value.ToString();
            }
        }

        public bool IsRunning { get; set; }

        #endregion

        public ItemTransfer(TransferSetting transferSetting, FormMain mainForm)
        {
            _TransferSetting = transferSetting;
            _MainForm = mainForm;
        }

        private bool SetSourceObjects()
        {
            try
            {
                sourceSiteCollection = new SPSite(_TransferSetting.SourceSiteCollection);
                sourceSite = sourceSiteCollection.AllWebs[_TransferSetting.SourceSite];
                sourceList = sourceSite.Lists[_TransferSetting.SourceList];
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            return false;
        }

        private bool SetDestinationObjects()
        {
            try
            {
                destinationSiteCollection = new SPSite(_TransferSetting.DestinationSiteCollection);
                destinationSite = destinationSiteCollection.AllWebs[_TransferSetting.DestinationSite];
                destinationList = GetDestinationList();

                if (destinationList == null)
                    return false;
                else
                    return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            return false;
        }

        private SPList GetDestinationList()
        {
            SPList dList = null;
            try
            {
                dList = destinationSite.Lists[_TransferSetting.DestinationList];
            }
            catch { }

            if (dList == null)
            {
                if (MessageBox.Show("Destination list not found!\nDo you want create destination list and copy items?", "Destination List Not Found", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
                {
                    CreateDestinationList();
                    dList = destinationSite.Lists[_TransferSetting.DestinationList];
                }
            }

            return dList;
        }

        private void CreateDestinationList()
        {
            SPListTemplate listTemplate = GetListTemplate();
            destinationSite.Lists.Add(_TransferSetting.DestinationList, "This list created by List Item Transfer application.", listTemplate);
            DeleteListTemplate();
        }

        private void DeleteListTemplate()
        {
            SPList listTemplateGallery = sourceSiteCollection.OpenWeb().Lists[ListTemplateGalleryName];
            foreach (SPListItem item in listTemplateGallery.Items)
            {
                if (item.Title == ListTemplateName)
                {
                    item.Delete();
                    break;
                }
            }
        }

        private SPListTemplate GetListTemplate()
        {
            sourceList.SaveAsTemplate(ListTemplateFileName, ListTemplateName, "", false);
            return sourceSiteCollection.GetCustomListTemplates(sourceSite)[ListTemplateName];
        }

        public void Start()
        {
            thread = new System.Threading.Thread(new System.Threading.ThreadStart(TransferProcess));
            thread.Start();
            IsRunning = true;
        }

        private void TransferProcess()
        {
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                if (!SetSourceObjects() ||
                    !SetDestinationObjects())
                {
                    SetFormForFinish();
                    return;
                }

                SetLogObjects();
                SetFormForStart();

                ItemCopied += new ItemCopiedEventHandler(ItemTransfer_ItemCopied);
                ItemNotCopied += new ItemNotCopiedEventHandler(ItemTransfer_ItemNotCopied);
                TransferItem();
                Finish(false, true);
            });
        }

        private void SetLogObjects()
        {
            log = new Log(sourceList.Title);
        }

        private void Finish(bool isCancelled, bool showResultWindow)
        {
            IsRunning = false;
            _MainForm.Show();
            _MainForm.WindowState = FormWindowState.Normal;

            if (showResultWindow)
                ShowResultWindow(isCancelled);

            SetFormForFinish();
        }

        private void ShowResultWindow(bool isCancelled)
        {
            string message = string.Format("Total item count: {1}{0}{0}Copied item count: {2}{0}{0}Failed item count: {3}", Environment.NewLine, TotalCount, CopiedCount, FailedCount);
            FormResult formResult = new FormResult();
            formResult.Owner = _MainForm;
            formResult.Message = message;
            formResult.IsCancelled = isCancelled;
            formResult.LogFilePath = log.GetLogFilePath();
            formResult.ShowDialog();
        }

        private void SetFormForFinish()
        {
            TotalCount = 0;
            CopiedCount = 0;
            FailedCount = 0;
            CurrentItem = string.Empty;

            ProgressBar.Maximum = 0;
            ProgressBar.Minimum = 0;
            ProgressBar.Value = 0;

            ButtonStartEnabled = true;
            ButtonCancelEnabled = false;
            ButtonPauseEnabled = false;
            InputEnabled = true;
        }

        private void IncrementProgressBar(bool isCopied)
        {
            if (!IsRunning) return;

            ProgressBar.Value++;

            if (isCopied)
                CopiedCount++;
            else
                FailedCount++;
        }

        private void ItemTransfer_ItemNotCopied(SPListItem sourceItem, Exception exception)
        {
            log.WriteLog(exception, string.Format("item id; {0}, item name; {1}, item url;  {2}/{3}", sourceItem.ID, sourceItem.Name, sourceSite.Url, sourceItem.Url));
            IncrementProgressBar(false);
        }

        private void ItemTransfer_ItemCopied(SPListItem sourceItem, SPListItem destinationItem)
        {
            IncrementProgressBar(true);
        }

        private void SetFormForStart()
        {
            TotalCount = sourceList.Items.Count;
            CopiedCount = 0;
            FailedCount = 0;

            ProgressBar.Maximum = TotalCount;
            ProgressBar.Minimum = 0;
            ProgressBar.Value = 0;

            ButtonCancelEnabled = true;
            ButtonPauseEnabled = true;
            InputEnabled = false;
        }

        private void TransferItem()
        {
            if (sourceList is SPDocumentLibrary)
                TransferDocumentLibraryItem();
            else if (sourceList is SPList)
                TransferCustomListItem();
        }

        private void TransferCustomListItem()
        {
            SPListItem destinationItem;
            foreach (SPListItem sourceItem in sourceList.Items)
            {
                try
                {
                    CurrentItem = sourceItem.Name;
                    destinationItem = destinationList.Items.Add();
                    SetFields(sourceItem, destinationItem);
                    SetAttachemnts(sourceItem, destinationItem);

                    destinationItem.Update();

                    ItemCopied(sourceItem, destinationItem);
                }
                catch (Exception ex)
                {
                    ItemNotCopied(sourceItem, ex);
                }
            }
        }

        private void SetAttachemnts(SPListItem sourceItem, SPListItem destinationItem)
        {
            SPFile file;
            foreach (string fileName in sourceItem.Attachments)
            {
                file = sourceItem.ParentList.ParentWeb.GetFile(string.Format("{0}{1}", sourceItem.Attachments.UrlPrefix, fileName));
                byte[] data = file.OpenBinary();
                destinationItem.Attachments.Add(fileName, data);
            }
        }

        private void SetFields(SPListItem sourceItem, SPListItem destinationItem)
        {
            foreach (SPField field in destinationItem.Fields)
            {
                if (IsValidField(field))
                    destinationItem[field.InternalName] = GetValue(sourceItem, field);
            }
        }

        private object GetValue(SPListItem sourceItem, SPField field)
        {
            object value = null;

            if (field is SPFieldUser)
            {
                if (sourceItem[field.InternalName] != null)
                    value = GetUsers(sourceItem[field.InternalName]);
            }
            else
            {
                value = sourceItem[field.InternalName];
            }

            return value;
        }

        private object GetUsers(object objectUsers)
        {
            string user;
            string users = "";
            if (objectUsers is SPFieldUserValueCollection)
            {
                SPFieldUserValueCollection userValueCollection = objectUsers as SPFieldUserValueCollection;
                foreach (SPFieldUserValue userValue in userValueCollection)
                {
                    if (userValue.User == null) continue;

                    user = GetUser(userValue.User.LoginName);
                    if (string.IsNullOrEmpty(user)) continue;

                    if (users == string.Empty)
                        users = user;
                    else
                        users = string.Format("{0}{1}{2}", users, SharePointSeperator, user);
                }
            }
            else
            {
                user = GetUser(GetUserName(objectUsers));
                if (!string.IsNullOrEmpty(user))
                    users = user;
            }

            return users;
        }

        private string GetUserName(object objectUsers)
        {
            int userId = int.Parse(objectUsers.ToString().Split(new string[] { SharePointSeperator }, StringSplitOptions.RemoveEmptyEntries)[0].Trim());
            SPUser user = null;

            try { user = sourceSite.SiteUsers.GetByID(userId); }
            catch { }

            return user == null ? string.Empty : user.LoginName;
        }

        private string GetUser(string userName)
        {
            string user = "";
            if (HasUser(userName))
                user = string.Format(UserParseFormat, userName);

            return user;
        }

        private bool HasUser(string userName)
        {
            SPUser user = null;
            try { user = destinationSite.EnsureUser(userName); }
            catch { }

            return user != null;
        }

        private bool IsValidField(SPField field)
        {
            if (field.ReadOnlyField)
                return false;

            if (ignoreFieldNames.Contains(field.InternalName))
                return false;

            return true;
        }

        private void TransferDocumentLibraryItem()
        {
            SetAssocationFields();
            string destinationFileUrl;
            SPListItem destinationItem;
            foreach (SPListItem sourceItem in sourceList.Items)
            {
                try
                {
                    CurrentItem = sourceItem.Name;
                    destinationFileUrl = string.Format("{0}/{1}/{2}", destinationSite.Url, destinationList.Title, sourceItem.Name);
                    sourceItem.CopyTo(destinationFileUrl);
                    destinationItem = GetCopiedItem(destinationFileUrl);
                    ValidateAssocationFields(sourceItem, destinationItem);

                    ItemCopied(sourceItem, destinationItem);
                }
                catch (Exception ex)
                {
                    ItemNotCopied(sourceItem, ex);
                }
            }
        }

        private void SetAssocationFields()
        {
            assocationFields = new List<FieldInfo>();
            if (sourceList.Items.Count == 0) return;

            foreach (SPField field in sourceList.Fields)
            {
                if (field.GetType() == typeof(SPFieldUser))
                    assocationFields.Add(new FieldInfo { DisplayName = field.Title, Name = field.InternalName, Type = FieldTypeEnum.UserOrGroup });
            }
        }

        private void ValidateAssocationFields(SPListItem sourceItem, SPListItem destinationItem)
        {
            if (assocationFields.Count == 0) return;

            foreach (FieldInfo fieldInfo in assocationFields)
                destinationItem[fieldInfo.Name] = GetValue(sourceItem, destinationItem.Fields[fieldInfo.DisplayName]);

            destinationItem.Update();
        }

        private SPListItem GetCopiedItem(string destinationFileUrl)
        {
            SPFile file = destinationSite.GetFile(destinationFileUrl);

            if (file == null)
                return null;
            else
                return file.Item;
        }

        public void Suspend()
        {
            thread.Suspend();
        }

        public void Resume()
        {
            thread.Resume();
        }

        public void Stop(bool showResultWindow)
        {
            if ((thread.ThreadState & System.Threading.ThreadState.Suspended) == System.Threading.ThreadState.Suspended)
                thread.Resume();

            thread.Abort();
            Finish(true, showResultWindow);
        }
    }
}
